;
; Copyright (c) Microsoft Corporation.  All rights reserved.
;
;
; Use of this sample source code is subject to the terms of the Microsoft
; license agreement under which you licensed this sample source code. If
; you did not accept the terms of the license agreement, you are not
; authorized to use this sample source code. For the terms of the license,
; please see the license agreement between you and Microsoft or, if applicable,
; see the LICENSE.RTF on your install media or the root of your tools installation.
; THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
;
;
; (C) Copyright 2006 Marvell International Ltd.
; All Rights Reserved
;
;
; INTEL CONFIDENTIAL
; Copyright 2004 - 2006 Intel Corporation. All Rights Reserved.
;
; The source code contained or described herein and all documents
; related to the source code (Material) are owned by Intel Corporation
; or its suppliers or licensors.  Title to the Material remains with
; Intel Corporation or its suppliers and licensors. The Material contains
; trade secrets and proprietary and confidential information of Intel
; or its suppliers and licensors. The Material is protected by worldwide
; copyright and trade secret laws and treaty provisions. No part of the
; Material may be used, copied, reproduced, modified, published, uploaded,
; posted, transmitted, distributed, or disclosed in any way without Intel's
; prior express written permission.
;
; No license under any patent, copyright, trade secret or other intellectual
; property right is granted to or conferred upon you by disclosure or
; delivery of the Materials, either expressly, by implication, inducement,
; estoppel or otherwise. Any license under such intellectual property rights
; must be express and approved by Intel in writing.
;
;
;*********************************************************************************
;
;  FILENAME:    xlli_LowLev_Init.s
;
;  PURPOSE:     This XLLI file contains the Low Level Initialization code used
;               to bring the PXA3xx processor out of reset. Optional Low
;               Level Initialzation code may be found in the xlli_LowLev_Init_opt.s
;               file.
;
;  LAST MODIFIED:  15-Aug-2006
;
;******************************************************************************
;
        INCLUDE  xlli_Monahans_defs.inc         ; Monahans specific include file
        INCLUDE  xlli_Littleton_defs.inc         ; Zylonite specific include file
;
        AREA    |.text|, CODE, READONLY, ALIGN=5         ; Align =5 required for "ALIGN 32" feature to work.
;
; List of Low Level Init functions in this source code include:
;
        EXPORT xlli_GPIO_init           ; Initialize the GPIO pins for this platform
        EXPORT xlli_MFPR_init           ; Initialize the Multi Function Pin Registers
        EXPORT xlli_MFPR_offset_table   ; MFPR offset lookup table
        EXPORT xlli_Dmem_initP1         ; Dynamic Memory Controller Initialization Part #1
        EXPORT xlli_Rcomp               ; Resistive compensation routine for DDR SDRAM
        EXPORT xlli_Dmem_initP2         ; Dynamic Memory Controller Initialization Part #2
        EXPORT xlli_Smem_init           ; Static Memory Controller Initialization 
        EXPORT xlli_U32Divide           ; Unsigned 32-bit division subroutine
        EXPORT xlli_DFI_offset_table    ; DFI Data Pin Offset Table


        LTORG
;
;*******************************************************************************
;
; ************************************
; **********                **********
; ********** xlli_GPIO_init **********  INIT THE GPIOs FOR THIS PLATFORM
; **********                **********
; ************************************
;
;       GPIO direction and output levels are set by this subroutine according
;       to the values in the platform include files. Output levels are set
;       so the platform will draw a minimal amount of power.
;
;       INPUT PARAMETERS:
;
;               NONE
;
;       RETURNED STATUS/VALUE(s):
;
;               NONE
;
xlli_GPIO_init  FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0-r2, lr}             ; Save r0-r2, & link on stack
                ENDIF

        ldr     r0,     =xlli_GPIOREGS_PHYSICAL_BASE    ; GPIO regs base address
;
;       Set any GPIOs that should be HIGH after GPIOs are configured.
;
        ldr     r1,     =xlli_GPSR0_value       ; Set bits for GPSR0
        str     r1,     [r0, #xlli_GPSR0_offset]; GPIOs 31:0

        ldr     r2,     =xlli_GPSR1_value       ; Set bits for GPSR1
        str     r2,     [r0, #xlli_GPSR1_offset]; GPIOs 63:32

        ldr     r1,     =xlli_GPSR2_value       ; Set bits for GPSR2
        str     r1,     [r0, #xlli_GPSR2_offset]; GPIOs 95:64

        ldr     r2,     =xlli_GPSR3_value       ; Set bits for GPSR3
        str     r2,     [r0, #xlli_GPSR3_offset]; GPIOs 127:96
;
;       Set any GPIOs that should be LOW after GPIOs are configured.
;
        ldr     r1,     =xlli_GPCR0_value       ; Set bits for GPCR0
        str     r1,     [r0, #xlli_GPCR0_offset]; GPIOs 31:0

        ldr     r2,     =xlli_GPCR1_value       ; Set bits for GPCR1
        str     r2,     [r0, #xlli_GPCR1_offset]; GPIOs 63:32

        ldr     r1,     =xlli_GPCR2_value       ; Set bits for GPCR2
        str     r1,     [r0, #xlli_GPCR2_offset]; GPIOs 95:64

        ldr     r2,     =xlli_GPCR3_value       ; Set bits for GPCR3
        str     r2,     [r0, #xlli_GPCR3_offset]; GPIOs 127:96
;
;       Set up GPIO direction registers.
;
        ldr     r1,     =xlli_GPDR0_value       ; Direction values for GPDR0
        str     r1,     [r0, #xlli_GPDR0_offset]; GPIOs 31:0

        ldr     r2,     =xlli_GPDR1_value       ; Direction values for GPDR1
        str     r2,     [r0, #xlli_GPDR1_offset]; GPIOs 63:32

        ldr     r1,     =xlli_GPDR2_value       ; Direction values for GPDR2
        str     r1,     [r0, #xlli_GPDR2_offset]; GPIOs 95:64

        ldr     r2,     =xlli_GPDR3_value       ; Direction values for GPDR3
        str     r2,     [r0, #xlli_GPDR3_offset]; GPIOs 127:96
;
;       GPIO Setup complete
;
                IF :DEF: xlli_stack_pointer
        ldmfd   sp!,    {r0-r2, pc}     ; Return to caller (via stack)
                ELSE
        mov     pc,     lr              ; Return to caller (no stack
                ENDIF

        ENDFUNC
        LTORG
;
;*******************************************************************************
;
; ************************************
; **********                **********
; ********** xlli_MFPR_init **********  INIT THE MFPRs FOR THIS PLATFORM
; **********                **********
; ************************************
;
;       The Multi-Function Pin registers are set to alternate function zero
;       and all other pin parameters are set so the platform will draw a
;       minimum amount of power.
;
;       INPUT PARAMETERS:
;
;               NONE
;
;       RETURNED STATUS/VALUE(s):
;
;               NONE
;

xlli_MFPR_init   FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0-r4, lr}             ; Save r0-r4, & link on stack
                ENDIF
;
;       Set all GPIO Multi Function Pin Registers (MFPRxx) to the values set
;       by the include file referenced in the code below. Since offsets to
;       MFPRs are not in a linear sequence, an offset lookup table below) is used.
;
        ;ldr     r0,     =xlli_MFPR_PHYSICAL_BASE; MFPR base address	; paramterize this for use in phy and virtual
        add     r4,     pc,     #xlli_MFPR_data - (.+8)         ; Address of data for MFPR settings 
;
;       The 4 line code segment below is a workaround for the commented out
;       line of code above. (Assembler bug - 8-Feb-05)
;
        ldr     r2,     =xlli_MFPR_offset_table ; Address of MFPR offset table   
        ldr     r3,     =xlli_MFPR_data         ; Address of MFPR data
        sub     r2,     r3,     r2              ; Calculate distance between the two
        sub     r2,     r4,     r2              ; R2 now contains address of the table
;
xlli_m0 ldrh    r3,     [r2],   #2              ; Get next offset value
        cmp     r3,     #0                      ; At end of table?
        beq     xlli_m1                         ; Branch if end of table <Offset = Zero>
        ldr     r1,     [r4],   #4              ; Fetch MFPR data from user table (below)
        str     r1,     [r0, r3]                ; Write MFPR data to MFPR
        b       xlli_m0                         ; Get next offset        
;
;       Perform a readback & return to caller
;
xlli_m1 sub     r2,     r2,     #2      ; Point back to MFPR for last table entry
        ldrhs   r3,     [r2]            ; Get the last offset value
        ldr     r4,     [r0, r3]        ; Read back to insure writes are complete

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        ldmfd   sp!,    {r0-r4, pc}             ; Return to caller via stack
                ELSE
        mov     pc,     lr                      ; Non-stack return
                ENDIF

        ENDFUNC
        LTORG
;
;       If xlli_Monahans_L is defined, use the MFPR lookup table below. Otherwise the
;       Monahans P MFPR table (further below) is used.
;

        IF      :DEF:   xlli_Monahans_L         ; MFPR table for Monahans L processors
;
;       This lookup table provides offsets to the Multi Function
;       Pin Registers (MFPRs), in ascending order, from GPIO_0 to GPIO_127
;       followed by the GPIO_0_2 to GPIO_6_2 MFPRs for Monahans_L
;
xlli_MFPR_offset_table                                                          ;        GPIOs
        DCW     0x0B4,  0x0B8,  0x0BC,  0x27C,  0x280,  0x284,  0x288,  0x28C   ; MFPRs   0->7
        DCW     0x290,  0x294,  0x298,  0x29C,  0x2A0,  0x2A4,  0x2A8,  0x2AC   ; MFPRs   8->15
        DCW     0x2B0,  0x2B4,  0x2B8,  0x2BC,  0x2C0,  0x2C4,  0x2C8,  0x2CC   ; MFPRs  16->23
        DCW     0x2D0,  0x2D4,  0x2D8,  0x400,  0x404,  0x408,  0x40C,  0x410   ; MFPRs  24->31
        DCW     0x414,  0x418,  0x41C,  0x420,  0x424,  0x428,  0x42C,  0x430   ; MFPRs  32->39
        DCW     0x434,  0x438,  0x43C,  0x440,  0x444,  0x448,  0x44C,  0x450   ; MFPRs  40->47
        DCW     0x454,  0x458,  0x45C,  0x460,  0x464,  0x468,  0x46C,  0x470   ; MFPRs  48->55
        DCW     0x474,  0x478,  0x47C,  0x480,  0x484,  0x488,  0x48C,  0x490   ; MFPRs  56->63
        DCW     0x494,  0x498,  0x49C,  0x4A0,  0x4A4,  0x4A8,  0x4AC,  0x4B0   ; MFPRs  64->71
        DCW     0x4B4,  0x4B8,  0x4BC,  0x4C0,  0x4C4,  0x4C8,  0x4CC,  0x4D0   ; MFPRs  72->79
        DCW     0x4D4,  0x4D8,  0X4DC,  0X4E0,  0x4E4,  0x4E8,  0x4EC,  0x4F0   ; MFPRs  80->87
        DCW     0x4F4,  0x4F8,  0x4FC,  0x500,  0x504,  0x508,  0x50C,  0x510   ; MFPRs  88->95
        DCW     0x514,  0x581,  0x51C,  0x600,  0x604,  0x608,  0x60C,  0x610   ; MFPRs  96->103
        DCW     0x614,  0x618,  0x61C,  0x620,  0x624,  0x628,  0x62C,  0x630   ; MFPRs 104->111
        DCW     0x634,  0x638,  0x63C,  0x640,  0x644,  0x648,  0x64C,  0x650   ; MFPRs 112->119
        DCW     0x654,  0x658,  0x65C,  0x660,  0x664,  0x668,  0x66C,  0x670   ; MFPRs 120->127
        DCW     0x674,  0x678,  0x2DC,  0x2E0,  0x2E4,  0x2E8,  0x2EC           ; MFPRs  0_2->6_2
        DCW     0x000                           ; 0x000 marks the end of the table
        ALIGN   4                               ; Insures 32-bit allignment

        ELSE    ; ******** If xlli_Monahans_L not defined, use PXA offsets below ********
;
;       This lookup table provides offsets to the Multi Function
;       Pin Registers (MFPRs), in ascending order, from GPIO_0 to GPIO_127
;       followed by the GPIO_0_2 to GPIO_17_2 MFPRs for the PXA
;
xlli_MFPR_offset_table                                                          ;        GPIOs
        DCW     0x124,  0x128,  0x12C,  0x130,  0x134,  0x28C,  0x290,  0x294   ; MFPRs   0->7
        DCW     0x298,  0x29C,  0x458,  0x2A0,  0x2A4,  0x2A8,  0x2AC,  0x2B0   ; MFPRs   8->15
        DCW     0x2B4,  0x2B8,  0x2BC,  0x2C0,  0x2C4,  0x2C8,  0x2CC,  0x2D0   ; MFPRs  16->23
        DCW     0x2D4,  0x2D8,  0x2DC,  0x400,  0x404,  0x408,  0x40C,  0x410   ; MFPRs  24->31
        DCW     0x414,  0x418,  0x41C,  0x420,  0x424,  0x428,  0x42C,  0x430   ; MFPRs  32->39
        DCW     0x434,  0x438,  0x43C,  0x440,  0x444,  0x448,  0x44C,  0x450   ; MFPRs  40->47
        DCW     0x454,  0x45C,  0x460,  0x464,  0x468,  0x46C,  0x470,  0x474   ; MFPRs  48->55
        DCW     0x478,  0x47C,  0x480,  0x484,  0x488,  0x48C,  0x490,  0x4B4   ; MFPRs  56->63
        DCW     0x4B8,  0x4BC,  0x4C0,  0x4C4,  0x4C8,  0x4CC,  0x4D0,  0x4D4   ; MFPRs  64->71
        DCW     0x4D8,  0x4DC,  0x4F0,  0x4F4,  0x4F8,  0x4FC,  0x500,  0x504   ; MFPRs  72->79
        DCW     0x508,  0x50C,  0X510,  0X514,  0x518,  0x51C,  0x520,  0x524   ; MFPRs  80->87
        DCW     0x528,  0x52C,  0x530,  0x534,  0x538,  0x53C,  0x540,  0x544   ; MFPRs  88->95
        DCW     0x548,  0x54C,  0x550,  0x600,  0x604,  0x608,  0x60C,  0x610   ; MFPRs  96->103
        DCW     0x614,  0x618,  0x61C,  0x620,  0x624,  0x628,  0x62C,  0x630   ; MFPRs 104->111
        DCW     0x634,  0x638,  0x63C,  0x640,  0x644,  0x648,  0x64C,  0x650   ; MFPRs 112->119
        DCW     0x654,  0x658,  0x65C,  0x660,  0x664,  0x668,  0x66C,  0x670   ; MFPRs 120->127
        DCW     0x674,  0x678,  0x67C,  0x680,  0x684,  0x688,  0x494,  0x498   ; MFPRs  0_2->7_2
        DCW     0x49C,  0x4A0,  0x4A4,  0x4A8,  0x4AC,  0x4B0,  0x4E0,  0x4E4   ; MFPRs  8_2->15_2
        DCW     0x4E8,  0x4EC                                                   ; MFPRs 16_2->17_2
        DCW     0x000                           ; 0x000 marks the end of the table
        ALIGN   4                               ; Insures 32-bit allignment

        ENDIF   ; ******** End of xlli_Monahans_L conditional ********
;
;       A user table is inserted below (as an INCLUDE file) to set up the MFPRs
;       at platform boot time. The MFPR values must be 32-bit values, in sequence, starting
;       with the MFPR value for GPIO_0 and ending with GPIO_127 (128 entries) followed by
;       18 more entries for GPIO_0_2 to GPIO_6_2.
;
;       The table provided with the XLLI code DOES NOT SET UP THE ALTERNATE FUNCTION BITS.
;       As provided, the MFPR table supplied with XLLI along with the GPIO register data
;       provided in the platform include file, will set the GPIO direction, the pin output
;       level (if the pin is configured as an output), pull up/down resistors and drive
;       strength. The configuration of the GPIOs after the xlli_GPIO_init code has completed
;       is designed to place the platform I/Os in a minimum current drain mode.
;
;       After xlli_GPIO_init has been called, it is left up to the user to configure the
;       alternate function bits as drivers are loaded/initialized. If referencing GPIOs by
;       GPIO number, the user may want to take advantage of the offset table above since it
;       is alreay loaded into memory.
;
xlli_MFPR_data                  ; MFPR data is loaded below via the platform include file

        INCLUDE         xlli_MFPR_table.inc     ; Platform MFPR settings are loaded here      
;
;*******************************************************************************
;
; **************************************
; **********                  **********
; ********** xlli_Dmem_InitP1 **********   INITALIZE DYNAMIC MEMORY CONTROLLER
; **********                  **********           (PART 1 OF 2 PARTS)
; **************************************
;
; The sequence below is based on the recommended memory initializing procedure
; recommended in the (Monahans) External Memory Controller Manual
;
;       INPUT PARAMETERS:
;
;               NONE
;
;       RETURNED STATUS/VALUE(s):
;
;               NONE
;

xlli_Dmem_initP1        FUNCTION
                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0-r4, lr}             ; Save r0-r4, & link on stack
                ENDIF
;
; STEP 1: Delay at least 100uS to allow internal clocks for SDCLK to stabilize.
;       This is a required hardware reset power-on wait period. XLLI code waits
;       a little bit longer - just to be sure.
;
        ldr     r0,     =xlli_OSTREGS_PHYSICAL_BASE     ; Load OS timer base address
        ldr     r2,     [r0, #xlli_OSCR0_offset]        ; Fetch starting value of OSCR0
        add     r2,     r2,     #0x150                  ; Actually, 0x145 is about 100uS
xlli_d1 ldr     r1,     [r0, #xlli_OSCR0_offset]        ; Fetch current OSCR0 value
        cmp     r1,     r2                              ; Is the timer past the time out value?
        bmi     xlli_d1                                 ; No - Loop until it is

;       Set up EMPI register
;
        ldr     r4,     =xlli_DMEMREGS_PHYSICAL_BASE    ; Dynamic Memory Control Reg Base
        ldr     r3,     =xlli_EMPI_value                ; Get EMPI value
        str     r3,     [r4, #xlli_EMPI_offset]         ; Load the EMPI register
;
; STEP 2: Prevent Power Down Command by setting CMA[APDD] bit. (A0 Silicon only - code commented out)
;
;        mov     r0,     #xlli_CMA_APDD                  ; Set this bit to prevent power down
;        str     r0,     [r4, #xlli_CMA_offset]          ; Set the bit
;
; STEP 3: Program MDCNFG then enable the occupied DRAM partitions.
;         MDCNFG[DMCEN] must remain deasserted!
;
        ldr     r2,     =xlli_MDCNFG_value_32           ; Force 32 bit DDR mode for PXA
        and     r0,     r2,     #xlli_MDCNFG_DSCE       ; Save the DSCE bits
        ldr     r3,     =0x8000077C                     ; Mask to clear reserved, DMCEN & DSCE bits
        and     r2,     r2,     r3                      ; Clear reserved, DMCEN & DSCE bits
        str     r2,     [r4, #xlli_MDCNFG_offset]       ; Write to controller
        ldr     r2,     [r4, #xlli_MDCNFG_offset]       ; Insure write is complete
        orr     r2,     r2,     r0                      ; Enable selected partition(s)
        str     r2,     [r4, #xlli_MDCNFG_offset]       ; Write to controller
        ldr     r2,     [r4, #xlli_MDCNFG_offset]       ; Insure write is complete
;
; Step 4: Wait a typical NOP power-up waiting period required by SDRAM (Typically
;         200 uS) Steps 5 & 6 can be executited during this waiting period. However,
;         xlli will do the wait now and then perform steps 5 & 6.
;
        ldr     r0,     =xlli_OSTREGS_PHYSICAL_BASE     ; Load OS timer base address
        ldr     r2,     [r0, #xlli_OSCR0_offset]        ; Fetch starting value of OSCR0
        add     r2,     r2,     #0x2A0                  ; Actually, 0x28A is about 200uS
xlli_m2 ldr     r1,     [r0, #xlli_OSCR0_offset]        ; Fetch current OSCR0 value
        cmp     r1,     r2                              ; Is the timer past the time out value?
        bmi     xlli_m2                                 ; No - Loop until it is
;
;       In early silicon it has been observed that the Rcomp eval may not complete
;       although the SWEVAL bit was set. The edits below will set this bit up to
;       four times before exiting.
;
        mov     r1,     #4              ; Set up Rcomp retry loop count
;
; STEP 5: Perform Resistive Compensation calibration cycle (RCOMP)
;
xlli_m3 ldr     r3,     =((xlli_RCOMP_RCRNG_value<<25) | xlli_RCOMP_REI)
        str     r3,     [r4, #xlli_RCOMP_offset]        ; Write inital values to RCOMP
        orr     r3,     r3,     #xlli_RCOMP_SWEVAL      ; Set bit to perform the RCOMP
        str     r3,     [r4, #xlli_RCOMP_offset]        ; Start the RCOMP evaluation
;
;       Wait for Rcomp evaluation cycle to complete - RCOMP[SWEVAL] clears.
;       A "time out" counter is in this loop - just in case the eval bit doesn't clear
;       so there is an exit path.
;
        mov     r0,     #0x10000                        ; Set up arbitrary time out counter
xlli_m4 ldr     r2,     [r4, #xlli_RCOMP_offset]        ; Fetch status
        subs    r0,     r0,     #1                      ; Decrement loop count
        beq     xlli_m9                                 ; Branch if timed out
        ands    r2,     r2,     #xlli_RCOMP_SWEVAL      ; Is the evaluation complete?
        bne     xlli_m4                                 ; No - loop until done.

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
xlli_m8 ldmfd   sp!,    {r0-r4, pc}             ; Return to caller via the stack
                ELSE
xlli_m8 mov     pc,     lr              	; Non stack return
                ENDIF

xlli_m9 subs    r1,     r1,     #1      ; Decrement re-try count
        bne     xlli_m3                 ; Try again if count is not zero
        b       xlli_m8                 ; Otherwise - time to give up on this!!!

        ENDFUNC
;
;*******************************************************************************
;
; ********************************
; **********            **********
; ********** xlli_Rcomp **********   PERFORM RCOMP CALCULATION (UPDATE PAD REGISTERS)
; **********            **********
; ********************************
;
; Performs Rcomp calculations based on PCODE and NCODE values presently in
; the DMCISR register.
;
;       INPUT PARAMETER REQUIRED:
;
;         R0 = Dynamic Memory Control Reg Base Address
;           If bit 0 of this address is set, the drive impedance of the clock signal
;           is reduced from 40 to 25 ohms in order to drive up to four devices.
;
;       RETURNED STATUS/VALUE(s):
;
;         none
;
;

xlli_Rcomp      FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0-r9, lr}             ; Save r0-r7, & link on stack
                ENDIF

        and     r9,     r0,     #1      ; Save bit 0 status in r9
        bic     r4,     r0,     #3      ; Save base address in r4 with bits 1:0 cleared
;
;       Obtain the NCODE and PCODE values from the DMCISR register and use this
;       starting value to calculate the final NSLEW and PSLEW values to be
;       programmed into each pad group.
;
        ldr     r2,     [r4, #xlli_DMCISR_offset]       ; Get results of the evaluation
        and     r6,     r2,     #0x003F8000             ; Extract NCODE
        mov     r6,     r6,     LSR #15                 ; and right justify
        and     r5,     r2,     #0x1FC00000             ; Extract PCODE
        mov     r5,     r5,     LSR #22                 ; and right justify
;
;       At this point: r5 = PCODE, r6 = NCODE
;
;       The PSLEW and NSLEW values are calculated as follows:
;
;       PSLEW = -2.96 - 0.0435(PCODE) + 0.348(NCODE)
;       NSLEW = 2.88 + 0.250(PCODE) - 0.125(NCODE)
;       PCODE and NCODE for the PAD_SDCLK registers is multiplied by 40/25
;
;       To gain the desired accuracy, the constant values above are multiplied
;       by 10,000 (so 0.0435 will become 435) and the math is done as integer math
;       rather than floating point. When the calculations are done, the result
;       is divided by 10,000 and rounded up/down to the closest integer value
;       for PSLEW and NSLEW.
;
;       Do the PSLEW calculation. 5000 is added to PSLEW before it is divided
;       by 10,000 so the result is rounded to the closest whole number.
;
        ldr     r3,     =3480           ; load 0.348 * 10,000 into r3
        mul     r2,     r3,     r6      ; r2 = 3480(NCODE)
        ldr     r3,     =435            ; load 0.0435 * 10,000 into r3
        mul     r1,     r3,     r5      ; r1 = 435(PCODE)
        sub     r2,     r2,     r1      ; subtract the PCODE from the NCODE
        ldr     r3,     =(29600 + 5000) ; load 2.96 * 10,000 + 5000 into r3
        sub     r0,     r2,     r3      ; r0 = PSLEW * 10,000 at this point
        ldr     r1,     =10000          ; Set up divisor
        bl      xlli_U32Divide          ; Divide!
        mov     r7,     r2              ; Save the calculated PSLEW value in r7
;
;       Do the NSLEW calculation. 5000 is added to NSLEW before it is divided
;       by 10,000 so the result is rounded to the closest whole number.
;
        ldr     r3,     =2500           ; load 0.250 * 10,000 into r3
        mul     r1,     r3,     r5      ; r1 = 250(PCODE)
        ldr     r3,     =1250           ; load 0.125 * 10,000 into r3
        mul     r2,     r3,     r6      ; r2 = 125(NCODE)
        sub     r1,     r1,     r2      ; Subtract the NCODE from the PCODE
        ldr     r3,     =(28800 + 5000) ; load 2.88 * 10,000 + 5000 into r3
        add     r0,     r2,     r3      ; r0 = NSLEW * 10,000 at this point
        ldr     r1,     =10000          ; Set up divisor
        bl      xlli_U32Divide          ; Divide!
        mov     r8,     r2              ; Save the calculated NSLEW value in r8
;
;       At this point the registers we care about contain the following:
;
;               r4 = Dynamic Memory Control Reg Base Address
;               r5 = PCODE value
;               r6 = NCODE value
;               r7 = PSLEW value
;               r8 = NSLEW value
;
;       Next step is to combine the NCODE, PCODE, NSLEW, PSLEW values and program
;       all the pad registers to the same value.
;
        mov     r5,     r5,     LSL #24 ; Move the PCODE into position (LSB to bit 24)
        mov     r6,     r6,     LSL #16 ; Move the NCODE into position (LSB to bit 16)
        mov     r7,     r7,     LSL #8  ; Move the PSLEW into position (LSB to bit 8)
        orr     r8,     r8,     r7      ; OR the NSLEW and PSLEW values together
        orr     r8,     r8,     r6      ; OR in the NCODE value
        orr     r8,     r8,     r5      ; OR in the PCODE value
;
;       The PAD register data has been assembled into r3. Now the code needs
;       to send this data to the selected PAD registers.
;

        str     r8,     [r4, #xlli_PAD_MA_offset]       ; Load the pad registers

        str     r8,     [r4, #xlli_PAD_MDMSB_offset]    ;

        str     r8,     [r4, #xlli_PAD_MDLSB_offset]    ;

        str     r8,     [r4, #xlli_PAD_DMEM_offset]     ;

        str     r8,     [r4, #xlli_PAD_SDCS_offset]     ;

        str     r8,     [r4, #xlli_PAD_SMEM_offset]     ;

        str     r8,     [r4, #xlli_PAD_SCLK_offset]     ;
;

;       Check to see if the clock drive strength is to be set for 40 or 25 ohms.

;       If this routine was called with bit 0 in the memory controller base address

;       set, change the clock drive impedance to 25 Ohms. (This is checked by seeing

;       if bit 0 in r9 is set or cleared).

;

        cmp     r9,     #0      ; Change clock drive impedance to 25 Ohms?

        beq     xlli_r5         ; No - skip over the following code

;

;       Adjust PCODE value for PAD_SDCLK register by 40/25

;

        mov     r3,     r8,     LSR #24 ; Get PCODE
        and     r3,     r3,     #0x7F   ; Mask out any other data
        mov     r2,     #40             ; Multiplier
        mul     r0,     r2,     r3      ; Multiply PCODE
        add     r0,     r0,     #12     ; Add 1/2 divisor value to take care of result rounding
        mov     r1,     #25             ; load divisor
        bl      xlli_U32Divide          ; Divide!
        mov     r2,     r2,     LSL #24         ; Move to PCODE position in register
        bic     r8,     r8,     #0x7F000000     ; Clear old PCODE bits
        orr     r8,     r8,     r2              ; OR in new PCODE bits
;
;       Adjust NCODE value for PAD_SDCLK register by 40/25
;
        mov     r3,     r8,     LSR #16 ; Get NCODE
        and     r3,     r3,     #0x7F   ; Mask out any other data
        mov     r2,     #40             ; Multiplier
        mul     r0,     r2,     r3      ; Multiply NCODE
        add     r0,     r0,     #12     ; Add 1/2 divisor value to take care of result rounding
        mov     r1,     #25             ; load divisor
        bl      xlli_U32Divide          ; Divide!
        mov     r2,     r2,     LSL #16         ; Move to NCODE position in register
        bic     r8,     r8,     #0x7F0000       ; Clear old NCODE bits
        orr     r8,     r8,     r2              ; OR in new NCODE bits

xlli_r5 str     r8,     [r4, #xlli_PAD_SDCLK_offset]    ; Write the PAD_SDCLK register
;
;       Set the UPDATE bit in the Rcomp register
;
        ldr     r3,     [r4, #xlli_RCOMP_offset]        ; Fetch RCOMP register value
        orr     r3,     r3,     #xlli_RCOMP_UPDATE      ; Set the UPDATE bit
        str     r3,     [r4, #xlli_RCOMP_offset]        ; Write RCOMP register back       
;
;       All done - return to caller
;
                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined,
        ldmfd   sp!,    {r0-r9, pc}             ; Return to caller via the stack
                ELSE
        mov     pc,     lr                      ; Non stack return
                ENDIF
        ENDFUNC  
;
;*******************************************************************************
;
; **************************************
; **********                  **********
; ********** xlli_Dmem_InitP2 **********   INITALIZE DYNAMIC MEMORY CONTROLLER
; **********                  **********           (PART 2 OF 2 PARTS)
; **************************************
;
; The sequence below is based on the recommended memory initializing procedure
; recommended in the (Monahans) External Memory Controller Manual
; (Issue 16860 - Section 1.4.3.6)
;
;       INPUT PARAMETER REQUIRED:
;
;               R0 = Dynamic Memory Control Reg Base Address
;
;       RETURNED STATUS/VALUE(s):
;
;               NONE
;

xlli_Dmem_initP2        FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0-r2, lr}             ; Save r0-r2, & link on stack
                ENDIF
;
; STEP 7: Write the Mode Register Set Config Register (MDMRS)
;
        ldr     r0,     =xlli_DMEMREGS_PHYSICAL_BASE    ; Dynamic Memory Control Reg Base
        ldr     r2,     =xlli_MDMRS_value               ; Get config values
        orr     r2,     r2,     #(xlli_MDMRS_MSCS0 | xlli_MDMRS_MDCOND)      ; Set the "Condition DRAM" bit
        str     r2,     [r0, #xlli_MDMRS_offset]        ; Update register
        ldr     r2,     [r0, #xlli_MDMRS_offset]        ; Insure write is complete
;
; STEP 8: Program the MDREFR with the desired refresh interval
;

        ldr     r1,     =xlli_MDREFR_value              ; Fetch inital refresh value
        str     r1,     [r0, #xlli_MDREFR_offset]       ; Write to register
        ldr     r1,     [r0, #xlli_MDREFR_offset]       ; Insure the write completes                           ; No - Loop until time runs out                              ; No - loop until they are
;
; STEP 9: Enable the dynamic memory controller
;
        ldr     r1,     [r0, #xlli_MDCNFG_offset]       ; Get the MDCNFG value
        orr     r1,     r1,     #xlli_MDCNFG_DMCEN      ; Set the enable bit
        str     r1,     [r0, #xlli_MDCNFG_offset]       ; Write to controller
        ldr     r1,     [r0, #xlli_MDCNFG_offset]       ; Insure write is complete
;
; STEP 10: Optionally Enable auto-power-down by clearing the CMA[APDD] bit
;
        mov     r2,     #0                      ; Clear a work register
        str     r2,     [r0, #xlli_CMA_offset]  ; Clear the bit
;
;       SDRAM should be ready for use at this point - return to caller       
;
                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        ldmfd   sp!,    {r0-r2, pc}             ; return to caller via the stack
                ELSE
        mov     pc,     lr                      ; Non stack return
                ENDIF
        
        ENDFUNC
;
;*******************************************************************************
;
; ************************************
; **********                **********
; ********** xlli_Smem_Init **********   INITALIZE STATIC MEMORY CONTROLLER
; **********                **********
; ************************************
;
; The sequence below is based on the recommended memory initializing procedure
; recommended in the (Monahans) Static Memory Controller, Chapter 2.4.6
;
;       INPUT PARAMETER REQUIRED:
;
;               R0 = Static Memory Control Reg Base Address
;
;       RETURNED STATUS/VALUE(s):
;
;               NONE
;
xlli_Smem_init  FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r1-r5, lr}             ; Save r1-r5, & link on stack
                ENDIF
;
;       GPIO 00 must be set to alternate function 1 to enable the RDY line
;       GPIO 02 must be set to alternate function 1 to enable chip select 3
;

        IF      :DEF:   xlli_Monahans_L ; Use the following code for Monahans L processors

        ldr     r1,     =(xlli_MFPR_PHYSICAL_BASE)      ; Pointer to MFP Register base

        ldr     r2,     [r1, #0xB4]     ; Get MFPR data for GPIO_0
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select RDY
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0xB4]     ; Write the data back

        ldr     r2,     [r1, #0xB8]     ; Get MFPR data for GPIO_1
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select nCS<2>
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0xB8]     ; Write the data back

        ldr     r2,     [r1, #0xBC]     ; Get MFPR data for GPIO_2
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select nCS<3>
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0xBC]     ; Write the data back

        ELSE    ; Use the following for Monahans P processors

        ldr     r1,     =(xlli_MFPR_PHYSICAL_BASE + 0x100)      ; Pointer to MFP Register base + 0x100

        ldr     r2,     [r1, #0x2C]     ; Get MFPR data for GPIO_2
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select RDY
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0x2C]     ; Write the data back

        ldr     r2,     [r1, #0x30]     ; Get MFPR data for GPIO_3
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select nCS<2>
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0x30]     ; Write the data back

        ldr     r2,     [r1, #0x34]     ; Get MFPR data for GPIO_4
        orr     r2,     r2,     #1      ; Set the alternate function to 1 to select nCS<3>
        bic     r2,     r2,     #0x8000 ; Clear the pull-select bit
        str     r2,     [r1, #0x34]     ; Write the data back

        ENDIF   ; ******** End of xlli_Monahans_L conditional ********
;
;       Set up the DFI GPIOs
;
;        add     r2,     pc,     #xlli_DFI_offset_table - (.+8) ; Address of offset table for MFPR settings
;
;       The above line of code will work on the next release of the Intel assembler
;       the following 2 lines of code are used to get around this bug.
;
;
        ldr     r5,     =xlli_DFI_offset_table - 16     ; Bug workaround
        ldr     r3,     =.                              ; Bug workaround
        sub     r2,     r5,     r3              ; Bug workaround
        add     r2,     pc,     r2              ; Bug workaround
;
;       The following code sets up the data flash data lines for use
;
        ldr     r1,     =xlli_MFPR_PHYSICAL_BASE        ; Pointer to MFP Register base
        ldr     r3,     =xlli_EMPI_MFPR_value           ; Value to load into MFPR's
        mov     r5,     #0x10                           ; Set up the 16 data lines
xlli_sm1
        ldrh    r4,     [r2],   #2      ; Fetch offset for next EMPI MFPR
        str     r3,     [r1, r4]        ; Load EMPI MFPR
        subs    r5,     r5,     #1      ; Decrement data line count
        bne     xlli_sm1                ; Get next offset if r5 is not zero
;
;       The following code segment sets up the remaining lines of the data flash
;       through hard coded values.
;

        IF      :DEF:   xlli_Monahans_L ; Run the following code for Monahans_L processors

        add     r1,     r1,     #0x200  ; Add base offset for the following MFPR registers
        mov     r2,     #0x40           ; Set the following GPIOs for alternate function 0
        str     r2,     [r1, #0x54]     ; nLLA
        str     r2,     [r1, #0x44]     ; nLUA
        str     r2,     [r1, #0x04]     ; nBE0
        str     r2,     [r1, #0x08]     ; nBE1
        str     r2,     [r1, #0x40]     ; CLE_nOE
        mov     r2,     #0x41           ; Set the following GPIO for alternate function 1
        str     r2,     [r1, #0x0C]     ; ALE_nWE

        ELSE    ; DO the following for the Monahans P processor
;
;       Set up DF_nADV1_ALE line
;
        ldr     r3,     [r1, #0x208]    ; MFPR base + offest to DF_nADV1_ALE pad register
        orr     r3,     r3,     #1      ; Set alternate function 1
        str     r3,     [r1, #0x208]    ; Write it back 

        ENDIF   ; ******** End of xlli_Monahans_L conditional ******** 
  
;
;       Program CSMSADRCFG for the bus mode being used on this platform
;
xlli_sm2
        ldr     r1,     =xlli_CSMSADRCFG_value          ; From platform include file
        str     r1,     [r0, #xlli_CSMSADRCFG_offset]   ; Write to register
;
;       Program the Address Configuration Registers CSADRCFG[0]-[3]
;
        ldr     r2,     =xlli_CSADRCFG0_value           ; Fetch from platform file
        str     r2,     [r0, #xlli_CSADRCFG0_offset]    ; Update CFG0 Register
        ldr     r1,     =xlli_CSADRCFG1_value           ; Fetch from platform file
        str     r1,     [r0, #xlli_CSADRCFG1_offset]    ; Update CFG1 Register
        ldr     r2,     =xlli_CSADRCFG2_value           ; Fetch from platform file
        str     r2,     [r0, #xlli_CSADRCFG2_offset]    ; Update CFG2 Register
        ldr     r1,     =xlli_CSADRCFG3_value           ; Fetch from platform file
        str     r1,     [r0, #xlli_CSADRCFG3_offset]    ; Update CFG3 Register
        ldr     r2,     =xlli_MSC1_value                ; Fetch value from platform file
        str     r2,     [r0, #xlli_MSC1_offset]         ; Update MSC1 register

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        ldmfd   sp!,    {r1-r5, pc}             ; return to caller via the stack
                ELSE
        mov     pc,     lr                      ; Non stack return
                ENDIF
xlli_DFI_offset_table

        IF      :DEF:   xlli_Monahans_L ; Offsets for Monahans_L

        DCW     0x220,  0x228,  0x230,  0x238,  0x258,  0x260,  0x268,  0x270   ; DF_IO0 -> DF_IO7
        DCW     0x224,  0x22C,  0x234,  0x23C,  0x25C,  0x264,  0x26C,  0x274   ; DF_I08 -> DF_IO15

        ELSE    ; The following table is used for Monahans P

        DCW     0x24C,  0x254,  0x25C,  0x264,  0x26C,  0x274,  0x27C,  0x284   ; DF_IO0 -> DF_IO7
        DCW     0x250,  0x258,  0x260,  0x268,  0x270,  0x278,  0x280,  0x288   ; DF_I08 -> DF_IO15

        ENDIF   ; ******** End of xlli_Monahans_L conditional ********

        ALIGN   4
;
;*********************************************************************************
;
;       ******************
;       *                * 
;       * xlli_U32Divide * Subroutine
;       *                *
;       ******************
;
; This subroutine performs an unsigned 32 bit divide
;
; PARAMETER PASSING:
;
; r0  contains the dividend (preserved)
; r1  contains the divisor  (preserved)
; r2  will contain the result on subroutine exit
; r3  will contain the remainder on subroutine exit 
;
xlli_U32Divide  FUNCTION

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined
        stmfd   sp!,    {r0, r1, r4, r5, lr}    ; Save used registers
                ENDIF

        mov     r2,     #0      ; Clear result
        mov     r3,     #0      ; Clear remainder
        mov     r5,     #32     ; load r5 with loop count
        mrs     r4,     CPSR                    ; Get CPSR
        bic     r4,     r4,     #0x20000000     ; Clear carry bit
;
;       Main division loop begins here
;
xlli_D1 msr     CPSR_f,         r4      ; write flags back
        adcs    r2,     r2,     r2      ; Shift C bit into result
        adcs    r0,     r0,     r0      ; Rotate r0 left 1 bit through C bit
        adcs    r3,     r3,     r3      ; Rotate data left, into r3
        subs    r3,     r3,     r1      ; Subtract dividend
        blt     xlli_D2                 ; Branch if negative
        mrs     r4,     CPSR            ; Get CPSR
        orr     r4,     r4,     #0x20000000     ; Set carry bit
        msr     CPSR_f,         r4      ; write flags back
        b       xlli_D3                 ; Check loop count

xlli_D2 add     r3,     r3,     r1      ; Restore r3
        mrs     r4,     CPSR            ; Get CPSR
        bic     r4,     r4,     #0x20000000     ; Clear carry bit
        msr     CPSR_f,         r4      ; write flags back

xlli_D3 mrs     r4,     CPSR            ; Get CPSR
        subs    r5,     r5,     #1      ; decrement loop count
        bne     xlli_D1                 ; Loop until done
;
;       Shift the last bit into the result and return to caller
;
        msr     CPSR_f,         r4      ; write flags back
        adcs    r2,     r2,     r2      ; Shift C bit into result
        adcs    r0,     r0,     r0      ; Rotate r0 left 1 bit through C bit

                IF :DEF: xlli_stack_pointer     ; If stack pointer has been defined,
        ldmfd   sp!,    {r0, r1, r4, r5, pc}    ; Return to caller vis the stack
                ELSE
        mov     pc,     lr                      ; Non stack return
                ENDIF
        ENDFUNC
        END